// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "sw/device/silicon_creator/lib/sigverify.h"

#include "sw/device/lib/base/bitfield.h"
#include "sw/device/lib/base/hardened.h"
#include "sw/device/lib/base/memory.h"
#include "sw/device/silicon_creator/lib/drivers/otp.h"
#include "sw/device/silicon_creator/lib/sigverify_mod_exp.h"

#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"
#include "otp_ctrl_regs.h"

/**
 * Checks the padding and the digest of an EMSA-PKCS1-v1_5 encoded message.
 *
 * EMSA-PKCS1-v1_5 is described in Section 9.2 of PKCS #1: RSA Cryptography
 * Specifications Version 2.2 (https://tools.ietf.org/html/rfc8017#section-9.2).
 * In PKCS#1, sequences are indexed from the leftmost byte and the first byte is
 * the most significant byte. An encoded message EM is an octet string of the
 * form:
 *    EM = 0x00 || 0x01 || PS || 0x00 || T, where
 * PS is a byte string of `0xff`s, T is the DER encoding of ASN.1 value of type
 * DigestInfo that contains the digest algorithm and the digest, and || denotes
 * concatenation. For SHA-256:
 *    T = (0x)30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H,
 * where H is the digest.
 *
 * This function checks the padding and the digest of an encoded message as
 * described in PKCS#1 but works on little-endian buffers.
 *
 * @param enc_msg An encoded message, little-endian.
 * @param act_digest Actual digest of the message being verified, little-endian.
 * @return Result of the operation.
 */
static rom_error_t sigverify_padding_and_digest_check(
    const sigverify_rsa_buffer_t *enc_msg, const hmac_digest_t *act_digest) {
  const uint32_t *enc_msg_ptr = enc_msg->data;

  if (memcmp(enc_msg_ptr, act_digest->digest, sizeof(act_digest->digest)) !=
      0) {
    return kErrorSigverifyBadEncodedMessage;
  }
  enc_msg_ptr += ARRAYSIZE(act_digest->digest);

  // Note: This also includes the zero byte right before PS.
  static const uint32_t kEncodedSha256[] = {
      0x05000420, 0x03040201, 0x86480165, 0x0d060960, 0x00303130,
  };
  if (memcmp(enc_msg_ptr, kEncodedSha256, sizeof(kEncodedSha256)) != 0) {
    return kErrorSigverifyBadEncodedMessage;
  }
  enc_msg_ptr += ARRAYSIZE(kEncodedSha256);

  // Note: `kPsLen` excludes the last word of `enc_msg`, which is 0x0001ffff.
  static const size_t kPsLen = ARRAYSIZE(enc_msg->data) -
                               ARRAYSIZE(kEncodedSha256) -
                               ARRAYSIZE(act_digest->digest) - /*last word*/ 1;
  uint32_t padding = UINT32_MAX;
  for (size_t i = 0; i < kPsLen; ++i) {
    padding &= *enc_msg_ptr++;
  }
  uint32_t res = ~padding;
  res |= *enc_msg_ptr ^ 0x0001ffff;
  if (res != 0) {
    return kErrorSigverifyBadEncodedMessage;
  }

  return kErrorOk;
}

/**
 * Determines whether the software implementation should be used for signature
 * verification.
 *
 * During manufacturing (TEST_UNLOCKED*), software implementation is used by
 * default since OTP may not have been programmed yet. The implementation to use
 * after manufacturing (PROD, PROD_END, DEV, RMA) is determined by the OTP
 * value.
 *
 * @param lc_state Life cycle state of the device.
 * @param[out] use_sw Use software implementation for signature verification.
 * @return Result of the operation.
 */
static rom_error_t sigverify_use_sw_rsa_verify(lifecycle_state_t lc_state,
                                               hardened_bool_t *use_sw) {
  switch (lc_state) {
    case kLcStateTestUnlocked0:
    case kLcStateTestUnlocked1:
    case kLcStateTestUnlocked2:
    case kLcStateTestUnlocked3:
    case kLcStateTestUnlocked4:
    case kLcStateTestUnlocked5:
    case kLcStateTestUnlocked6:
    case kLcStateTestUnlocked7:
      // Don't read from OTP during manufacturing. Use software
      // implementation by default.
      *use_sw = kHardenedBoolTrue;
      return kErrorOk;
    case kLcStateProd:
    case kLcStateProdEnd:
    case kLcStateDev:
    case kLcStateRma:
      *use_sw =
          otp_read32(OTP_CTRL_PARAM_CREATOR_SW_CFG_USE_SW_RSA_VERIFY_OFFSET);
      return kErrorOk;
    default:
      return kErrorSigverifyBadLcState;
  }
}

rom_error_t sigverify_rsa_verify(const sigverify_rsa_buffer_t *signature,
                                 const sigverify_rsa_key_t *key,
                                 const hmac_digest_t *act_digest,
                                 lifecycle_state_t lc_state) {
  hardened_bool_t use_sw;
  RETURN_IF_ERROR(sigverify_use_sw_rsa_verify(lc_state, &use_sw));

  sigverify_rsa_buffer_t enc_msg;
  switch (use_sw) {
    case kHardenedBoolTrue:
      RETURN_IF_ERROR(sigverify_mod_exp_ibex(key, signature, &enc_msg));
      break;
    case kHardenedBoolFalse:
      RETURN_IF_ERROR(sigverify_mod_exp_otbn(key, signature, &enc_msg));
      break;
    default:
      return kErrorSigverifyBadOtpValue;
  }
  RETURN_IF_ERROR(sigverify_padding_and_digest_check(&enc_msg, act_digest));

  return kErrorOk;
}

void sigverify_usage_constraints_get(
    uint32_t selector_bits, manifest_usage_constraints_t *usage_constraints) {
  usage_constraints->selector_bits = selector_bits;
  lifecycle_device_id_get(&usage_constraints->device_id);
  // TODO(#7948): Define OTP entries for manufacturing states. Left unselected
  // for now.
  usage_constraints->manuf_state_creator =
      MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL;
  usage_constraints->manuf_state_owner =
      MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL;
  usage_constraints->life_cycle_state = lifecycle_state_get();

  static_assert(
      kManifestSelectorBitDeviceIdFirst == 0 &&
          kManifestSelectorBitDeviceIdLast == kLifecycleDeviceIdNumWords - 1,
      "mapping from selector_bits to device_id changed, loop must be updated");
  for (size_t i = 0; i < kLifecycleDeviceIdNumWords; ++i) {
    if (!bitfield_bit32_read(selector_bits, i)) {
      usage_constraints->device_id.device_id[i] =
          MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL;
    }
  }
  if (!bitfield_bit32_read(selector_bits,
                           kManifestSelectorBitManufStateCreator)) {
    usage_constraints->manuf_state_creator =
        MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL;
  }
  if (!bitfield_bit32_read(selector_bits,
                           kManifestSelectorBitManufStateOwner)) {
    usage_constraints->manuf_state_owner =
        MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL;
  }
  if (!bitfield_bit32_read(selector_bits, kManifestSelectorBitLifeCycleState)) {
    usage_constraints->life_cycle_state =
        MANIFEST_USAGE_CONSTRAINT_UNSELECTED_WORD_VAL;
  }
}

// `extern` declarations for `inline` functions in the header.
extern uint32_t sigverify_rsa_key_id_get(const sigverify_rsa_buffer_t *modulus);
